package com.Register.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.json.JSONUtil;
import com.Register.context.BaseContext;
import com.Register.dto.*;
import com.Register.entity.EnrollActivity;
import com.Register.entity.VolunteerActivity;
import com.Register.es.ActivityDoc;
import com.Register.mapper.EnrollActivityMapper;
import com.Register.mapper.VolunteerActivityMapper;
import com.Register.mapper.WxUserMapper;
import com.Register.result.PageResult;
import com.Register.result.Result;
import com.Register.service.EnrollActivityService;
import com.Register.service.VolunteerActivityService;
import com.Register.vo.ActivityPageQueryVO;
import com.Register.vo.MyActivityPageQueryVO;
import com.Register.vo.SearchActivityByIdVO;
import com.alibaba.excel.EasyExcel;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.elasticsearch.action.delete.DeleteRequest;
import org.elasticsearch.action.index.IndexRequest;
import org.elasticsearch.action.update.UpdateRequest;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.common.xcontent.XContentType;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.TimeUnit;

import static com.Register.constant.RedisConstant.*;
import static com.Register.constant.RedisConstant.activityId;
import static com.Register.listener.EnrollActivityListener.*;
import static com.Register.listener.LoadActivityDocListener.LOAD_ACTIVITY_DOC_EXCHANGE_NAME;
import static com.Register.listener.LoadActivityDocListener.LOAD_ACTIVITY_DOC_ROUTING_KEY;

@Service
@Slf4j
public class VolunteerActivityServiceImpl extends ServiceImpl<VolunteerActivityMapper, VolunteerActivity> implements VolunteerActivityService {

    @Resource
    private VolunteerActivityMapper volunteerActivityMapper;

    @Resource
    private WxUserMapper wxUserMapper;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private EnrollActivityMapper enrollActivityMapper;

    // 创建 ObjectMapper 实例
    @Resource
    private ObjectMapper objectMapper = new ObjectMapper();

    @Resource
    private RedissonClient redissonClient;

    @Resource
    private EnrollActivityService enrollActivityService;

    @Resource
    private RabbitTemplate rabbitTemplate;

    @Resource
    private RestHighLevelClient client;

    /**
     * 创建志愿活动
     */
    @Override
    @Transactional
    public void CreateActivity(CreateActivityDTO createActivityDTO) throws JsonProcessingException {
        log.info("前端传回的对象为:{}",createActivityDTO);
        //创建活动对象
        VolunteerActivity volunteerActivity=new VolunteerActivity();
        //拷贝前端传来的字段到活动对象中
        BeanUtil.copyProperties(createActivityDTO,volunteerActivity);
        //设置缺少的字段
        volunteerActivity.setCreateUser(BaseContext.getCurrentId());
        volunteerActivity.setCreateTime(LocalDateTime.now());
        volunteerActivity.setAddNumber(0);
        save(volunteerActivity);
        ActivityPageQueryVO activityPageQueryVO=ActivityPageQueryVO.builder()
                .id(volunteerActivity.getId())
                .activityName(volunteerActivity.getActivityName())
                .organization(volunteerActivity.getOrganization())
                .remainder(String.valueOf(volunteerActivity.getRecruitNumber() - volunteerActivity.getAddNumber()))
                .beginTime(volunteerActivity.getBeginTime())
                .endTime(volunteerActivity.getEndTime())
                .recruitNumber(volunteerActivity.getRecruitNumber())
                .build();
        if(createActivityDTO.getTag()!=null){
            activityPageQueryVO.setTag(Arrays.asList(createActivityDTO.getTag().split("[,，]")).toString());
        }
        //拷贝要写入的对象
        ActivityDoc activityDoc=BeanUtil.copyProperties(activityPageQueryVO, ActivityDoc.class);
        //异步更新es文档
        rabbitTemplate.convertAndSend(LOAD_ACTIVITY_DOC_EXCHANGE_NAME,LOAD_ACTIVITY_DOC_ROUTING_KEY,activityDoc);
        //保存活动信息到redis中
        stringRedisTemplate.opsForValue().set(activityId+volunteerActivity.getId(),objectMapper.writeValueAsString(activityPageQueryVO));
        //更新活动id列表
        stringRedisTemplate.opsForList().leftPush(activityIdList,volunteerActivity.getId().toString());
    }

    /**
     * 分页查询活动信息
     */
    @Override
    public PageResult ActivityPageQuery(ActivityPageQueryDTO activityPageQueryDTo) throws JsonProcessingException {
        Long page=activityPageQueryDTo.getPageNo();
        Long pageSize=activityPageQueryDTo.getPageSize();
        //获取活动id列表的范围
        List<String> idList=stringRedisTemplate.opsForList().range(activityIdList,(page - 1) * pageSize, page * pageSize - 1);
        //如果id条件不在列表中就手动加入
        if(activityPageQueryDTo.getId()!=null){
            assert idList != null;
            if(!idList.contains(activityPageQueryDTo.getId().toString())){
                idList.add(activityPageQueryDTo.getId().toString());
            }
        }
        //如果id列表为空，返回空分页结果
        if(idList==null||idList.isEmpty()){
            return new PageResult(0L,0L, Collections.emptyList());
        }
        //根据活动id获取活动详情
        List<ActivityPageQueryVO> activityPageQueryVOList=new ArrayList<>();
        for(String id:idList){
            String activityKey=activityId+id;
            String cachedActivity=stringRedisTemplate.opsForValue().get(activityKey);
            if(cachedActivity!=null){
                ActivityPageQueryVO activityVO = objectMapper.readValue(cachedActivity, ActivityPageQueryVO.class);
                // 动态筛选
                if (isMatching(activityPageQueryDTo, activityVO)) {
                    activityPageQueryVOList.add(activityVO);
                }
            }
            //如果缓存没有该数据就去数据库中查找
            else {
                //根据条件查找
                LambdaQueryWrapper<VolunteerActivity> lambdaQueryWrapper=new LambdaQueryWrapper<>();
                lambdaQueryWrapper
                        .eq(VolunteerActivity::getId,id)
                        .eq(activityPageQueryDTo.getActivityName()!=null,
                                VolunteerActivity::getActivityName,activityPageQueryDTo.getActivityName())
                        .eq(activityPageQueryDTo.getOrganization()!=null,
                                VolunteerActivity::getOrganization,activityPageQueryDTo.getOrganization())
                        .ge(activityPageQueryDTo.getBeginTime()!=null,
                                VolunteerActivity::getBeginTime,activityPageQueryDTo.getBeginTime())
                        .le(activityPageQueryDTo.getEndTime()!=null,
                                VolunteerActivity::getEndTime,activityPageQueryDTo.getEndTime());
                VolunteerActivity volunteerActivity=getOne(lambdaQueryWrapper);
                if(volunteerActivity!=null) {
                    ActivityPageQueryVO activityPageQueryVO = ActivityPageQueryVO.builder()
                            .id(volunteerActivity.getId())
                            .activityName(volunteerActivity.getActivityName())
                            .organization(volunteerActivity.getOrganization())
                            .remainder(String.valueOf(volunteerActivity.getRecruitNumber() - volunteerActivity.getAddNumber()))
                            .beginTime(volunteerActivity.getBeginTime())
                            .endTime(volunteerActivity.getEndTime())
                            .recruitNumber(volunteerActivity.getRecruitNumber())
                            .build();
                    if(volunteerActivity.getTag()!=null){
                        activityPageQueryVO.setTag(Arrays.asList(volunteerActivity.getTag().split("[,，]")).toString());
                    }
                    //加入缓存中，方便下次查询
                    //保存活动信息到redis中
                    stringRedisTemplate.opsForValue().set(activityId+volunteerActivity.getId(),objectMapper.writeValueAsString(activityPageQueryVO));
                    activityPageQueryVOList.add(activityPageQueryVO);
                }
            }
        }
        // 返回分页结果
        long total = stringRedisTemplate.opsForList().size(activityIdList); // 获取列表总长度
        long pages = (total + pageSize - 1) / pageSize; // 计算总页数
        return new PageResult(pages, total, activityPageQueryVOList);
    }

    /**
     *动态筛选
     */
    private boolean isMatching(ActivityPageQueryDTO queryDTO, ActivityPageQueryVO activityVO) {
        return (queryDTO.getId() == null || queryDTO.getId().equals(activityVO.getId()))
                && (queryDTO.getActivityName() == null || activityVO.getActivityName().contains(queryDTO.getActivityName()))
                && (queryDTO.getOrganization() == null || activityVO.getOrganization().contains(queryDTO.getOrganization()))
                && (queryDTO.getBeginTime() == null || !activityVO.getBeginTime().isAfter(queryDTO.getBeginTime()))
                && (queryDTO.getEndTime() == null || !activityVO.getEndTime().isBefore(queryDTO.getEndTime()));
    }


    /**
     * 管理员更新活动相关信息
     */
    @Override
    @Transactional
    public Result<String> UpdateActivity(ActivityUpdateDTO activityUpdateDTO) throws IOException, ParseException {
        //获取活动对象
        VolunteerActivity volunteerActivity=volunteerActivityMapper.selectById(activityUpdateDTO.getId());
        //拷贝属性,设置更新
        volunteerActivity.setUpdateTime(LocalDateTime.now());
        volunteerActivity.setUpdateUser(BaseContext.getCurrentId());
        BeanUtil.copyProperties(activityUpdateDTO,volunteerActivity);
        //进行判断，检查管理员的更新操作
        if(activityUpdateDTO.getRecruitNumber()!=null&&
                activityUpdateDTO.getRecruitNumber()<volunteerActivity.getAddNumber()){
            return Result.error("修改后的招募人数不能小于已经报名人数");
        }
        if(activityUpdateDTO.getRegistrationDeadline()!=null
                &&activityUpdateDTO.getRegistrationDeadline().isBefore(LocalDateTime.now())){
            return Result.error("设置的截至时间不能在当前时间之前");
        }
        if(activityUpdateDTO.getDeleted()!=null&&activityUpdateDTO.getDeleted()==1){
            volunteerActivityMapper.deleteById(activityUpdateDTO.getId());
            //删除缓存
            stringRedisTemplate.delete(activityId+volunteerActivity.getId());
            stringRedisTemplate.opsForList().remove(activityIdList,0,volunteerActivity.getId().toString());
            //删除对应的索引
            DeleteRequest request = new DeleteRequest("activities", String.valueOf(volunteerActivity.getId()));
            //发送删除请求
            client.delete(request,RequestOptions.DEFAULT);
            return Result.success("删除成功");
        }
        //更新到数据库
        volunteerActivityMapper.updateById(volunteerActivity);
        //删除缓存
        stringRedisTemplate.delete(activityId+volunteerActivity.getId());
        //更新对应的文档索引
        UpdateRequest request = new UpdateRequest("activities", String.valueOf(volunteerActivity.getId()));
        // 转换时间参数
        Long newBeginTime = convertToTimestamp(activityUpdateDTO.getBeginTime());
        Long newEndTime = convertToTimestamp(activityUpdateDTO.getEndTime());

// 将参数传递给 request.doc
        request.doc(
                "activityName", activityUpdateDTO.getActivityName(),
                "organization", activityUpdateDTO.getOrganization(),
                "recruitNumber", activityUpdateDTO.getRecruitNumber(),
                "tag", activityUpdateDTO.getTag(),
                "beginTime", newBeginTime,
                "endTime", newEndTime
        );

// 发送请求
        client.update(request, RequestOptions.DEFAULT);
        return Result.success("更新成功");
    }

    // 时间格式转换为时间戳（毫秒）
    private static Long convertToTimestamp(String time) throws ParseException {
        SimpleDateFormat originalFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        Date date = originalFormat.parse(time);
        return date.getTime();
    }



    /**
     * 用户参加活动
     * @param enrollActivityDTO
     * @return
     */
    @Override
    @Transactional
    public Result<String> EnrollActivity(EnrollActivityDTO enrollActivityDTO) {
        //查询相关操作不需要加锁
        //1.先查询活动
        VolunteerActivity volunteerActivity = getById(enrollActivityDTO.getActivityId());
        if (volunteerActivity == null) {
            return Result.error("活动id无效");
        }
        //2.判断是否在有效报名时间
        if (LocalDateTime.now().isAfter(volunteerActivity.getRegistrationDeadline())) {
            return Result.error("报名时间已经截止");
        }
        //3.判断招募人数是否已经足够
        if (volunteerActivity.getAddNumber() >= volunteerActivity.getRecruitNumber()) {
            return Result.error("报名人数已经达到上限，无法报名");
        }
        //4.判断用户是否已经报名了
        Long count=enrollActivityService.query()
                .eq("user_id",BaseContext.getCurrentId())
                .eq("activity_id",enrollActivityDTO.getActivityId())
                .count();
        if(count>0){
            return Result.error("您已经报名活动了，无需再次报名");
        }
        //异步处理创建活动
        String userId = BaseContext.getCurrentId().toString();
        String lockKey = "activity_lock:" + enrollActivityDTO.getActivityId() + ":" + userId; // 锁粒度为活动ID + 用户ID
        RLock lock = redissonClient.getLock(lockKey); // 获取 Redisson 锁
        try {
            boolean lockAcquired = lock.tryLock(30, 30, TimeUnit.SECONDS); // 设置锁的最大等待时间和持有时间
            if (!lockAcquired) {
                return Result.error("当前活动报名正在处理，请稍后再试");
            }
            EnrollActivityTempDTO enrollActivityTempDTO=EnrollActivityTempDTO.builder()
                    .userId(BaseContext.getCurrentId())
                    .activityId(enrollActivityDTO.getActivityId())
                    .beginTime(volunteerActivity.getBeginTime())
                    .endTime(volunteerActivity.getEndTime())
                    .build();
            rabbitTemplate.convertAndSend(CREATE_SELFACTIVITY_SETACTIVITY_EXCHANGE_NAME,CREATE_SELFACTIVITY_SETACTIVITY_ROUTING_KEY,enrollActivityTempDTO);
            return Result.success("报名请求已提交，正在处理");
        }catch (InterruptedException e){
            Thread.currentThread().interrupt();
            return Result.error("系统繁忙，请稍后再试");
        }finally {
            // 释放锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 创建活动类
     *
     * @param enrollActivityDTO 传入活动id
     */
    @Transactional
    @Override
    public void createEnrollActivity(EnrollActivityTempDTO enrollActivityDTO){
        //5.添加报名人数
        boolean success=update()
                .setSql("add_number=add_number+1")
                .eq("id",enrollActivityDTO.getActivityId())
                //确保报名人数不会超过招募人数
                .gt("recruit_number-add_number",0)
                .update();
        if(!success){
            //添加报名人数
            Result.error("报名人数已经达到上限，无法报名");
            return;
        }
        Long userId= enrollActivityDTO.getUserId();
        //6.添加到用户报名表
        EnrollActivity enrollActivity=new EnrollActivity();
        enrollActivity.setActivityId(enrollActivityDTO.getActivityId());
        enrollActivity.setUserId(userId);
        enrollActivity.setBeginTime(enrollActivityDTO.getBeginTime());
        enrollActivity.setEndTime(enrollActivityDTO.getEndTime());
        enrollActivity.setCreateTime(LocalDateTime.now());
        enrollActivityService.save(enrollActivity);
        //删除对应的redis缓存
        stringRedisTemplate.delete(activityId+enrollActivity.getActivityId());
        Result.success("报名成功");
    }

    @Override
    public void exportUserInfoToExcel(Long activityId, HttpServletResponse httpServerResponse) {
        try {
            //获取活动名称
            String activityName = volunteerActivityMapper.getActivityNameById(activityId);
            if(activityName==null){
                httpServerResponse.setContentType("application/json");
                httpServerResponse.setCharacterEncoding(StandardCharsets.UTF_8.name());
                httpServerResponse.getWriter().write("{\"code\":\"0\",\"error\": \"暂无该活动的信息\"}");
                return;
            }
            //获取报名用户信息并导出为DTO
            List<Long> wxUserId = enrollActivityMapper.getUserId(activityId);
            if(wxUserId.isEmpty()){
                httpServerResponse.setContentType("application/json");
                httpServerResponse.setCharacterEncoding(StandardCharsets.UTF_8.name());
                httpServerResponse.getWriter().write("{\"code\":\"0\",\"error\": \"该活动暂无人员报名，无法导出\"}");
                return;
            }
            List<UserExportDTO> exportDTOS = new ArrayList<>();
            //构造表单对象
            for (Long id : wxUserId) {
                UserExportDTO exportDTO = wxUserMapper.selectExportDTOById(id);
                exportDTOS.add(exportDTO);
            }
            //设置响应头，指定文件名
            String fileName = URLEncoder.encode(activityName, StandardCharsets.UTF_8) + ".xlsx";
            httpServerResponse.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
            httpServerResponse.setCharacterEncoding(StandardCharsets.UTF_8.name());
            httpServerResponse.setHeader("Content-Disposition", "attachment; filename=" + fileName);
            //4.使用EasyExcel写入数据
            EasyExcel.write(httpServerResponse.getOutputStream(), UserExportDTO.class)
                    .sheet("报名用户信息")
                    .doWrite(exportDTOS);
        }catch (Exception e){
            e.printStackTrace();
            try {
                httpServerResponse.setContentType("application/json");
                httpServerResponse.setCharacterEncoding(StandardCharsets.UTF_8.name());
                httpServerResponse.getWriter().write("{\"code\":\"0\",\"error\": \"导出失败，请稍后重试\"}");
            } catch (IOException ioException) {
                ioException.printStackTrace();
            }
        }
    }


    /**
     * 我的志愿活动分页查询
     */
    @Override
    public PageResult getActivitiesByUserId(int currentPage, Long pageSize,Integer status) {
        //获取用户id
        Long userId= BaseContext.getCurrentId();
        // 计算分页的 offset
        Long offset = (currentPage - 1) * pageSize;

        // 查询活动数据
        List<MyActivityPageQueryVO> activityList = volunteerActivityMapper.getActivitiesByUserId(userId, offset, pageSize,status);
        log.info("查到的数据为:{}",activityList);
        // 获取总记录数（你可以通过执行 COUNT 查询来获得）
        LambdaQueryWrapper<EnrollActivity> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper
                .eq(EnrollActivity::getUserId,userId)
                .eq(status!=null,EnrollActivity::getStatus,status);
        long total = enrollActivityService.count(lambdaQueryWrapper);

        // 创建分页对象并返回
        return new PageResult(total,pageSize,activityList);
    }

    /**
     * 根据id查询志愿活动
     * @param activityId
     * @return
     */
    @Override
    public SearchActivityByIdVO searchActivityById(Long activityId) {
        VolunteerActivity volunteerActivity=getById(activityId);
        SearchActivityByIdVO searchActivityByIdVO=new SearchActivityByIdVO();
        BeanUtil.copyProperties(volunteerActivity,searchActivityByIdVO);
        return searchActivityByIdVO;
    }

    /**
     * 用户退出报名志愿活动
     */
    @Override
    @Transactional
    public Result<String> quitActivity(EnrollActivityDTO enrollActivityDTO) {
        //将数据库中的用户已报名信息设为deleted状态
        LambdaQueryWrapper<EnrollActivity> lambdaQueryWrapper=new LambdaQueryWrapper<>();
        lambdaQueryWrapper
                .eq(EnrollActivity::getActivityId,enrollActivityDTO.getActivityId())
                .eq(EnrollActivity::getUserId,BaseContext.getCurrentId());
        EnrollActivity enrollActivity= enrollActivityService.getOne(lambdaQueryWrapper);
        if(enrollActivity==null){
            return Result.error("您未报名此活动");
        }
        enrollActivityMapper.deleteById(enrollActivity.getId());
        //修改已报名人数
        VolunteerActivity volunteerActivity=getById(enrollActivityDTO.getActivityId());
        if(volunteerActivity.getAddNumber()>0) {
            boolean success = update()
                    .setSql("add_number=add_number-1")
                    .eq("id", volunteerActivity.getId())
                    .update();
            if(success){
                return Result.success("您已成功取消报名");
            }
        }
        return Result.error("取消报名失败");
    }

    @Override
    public void addDocument(ActivityDoc activityDoc) throws IOException {
        //1.将activityDoc转为Json
        String doc= JSONUtil.toJsonStr(activityDoc);
        //2.准备request对象
        IndexRequest request = new IndexRequest("activities").id(activityDoc.getId());
        //3.载入json文档
        request.source(doc, XContentType.JSON);
        //4.发送请求
        client.index(request, RequestOptions.DEFAULT);
    }


}
